﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.ComponentModel;
using System.Drawing;
using System.Windows.Forms;

namespace simDC
{

    public class Encodings
    {
        
        public static Dictionary<String, Int32> instructionDictionary = new Dictionary<String, Int32>() {
                        
                        // B1 class instructions -instruction 2 operands, opcode - 4 bit 

                         { "mov", 0x0000 },
                         { "add", 0x1000 },
                         { "sub", 0x2000 },
                         { "cmp", 0x3000 },
                         { "and", 0x4000 },
                         { "or",  0x5000 },
                         { "xor", 0x6000 },
                         { "mul", 0x7000 },

                         // B2 class instructions - instr. 1 operand,  opcode - 10 bit 

                         { "clr", 0x8000 },
                         { "neg", 0x8100 },
                         { "inc", 0x8200 },
                         { "dec", 0x8300 },
                         { "asl", 0x8400 },
                         { "asr", 0x8500 },
                         { "lsr", 0x8600 },
                         { "rol", 0x8700 },
                         { "ror", 0x8800 },
                         { "rlc", 0x8900 },
                         { "rrc", 0x8A00 },
                         { "jmp", 0x8B00 },
                         { "push", 0x8C00 },
                         { "pop", 0x8D00 },
                         { "call", 0x8E00 },

                          // B3 class instructions - branch instruction ,  opcode - 8 bit 
                          
                         { "br", 0xA000 },
                         { "bne", 0xA100 },
                         { "beq", 0xA200 },
                         { "bpl", 0xA300 },
                         { "bmi", 0xA400 },
                         { "bcs", 0xA500 },
                         { "bcc", 0xA600 },
                         { "bvs", 0xA700 },
                         { "bvc", 0xA800 },  
 
                          // B4 class instructions - instr. without operands,  opcode - 16 bit

                         { "clc", 0xC000 },
                         { "clv", 0xC100 },
                         { "clz", 0xC200 },
                         { "cls", 0xC300 },
                         { "ccc", 0xC400 },
                         { "sec", 0xC500 },
                         { "sev", 0xC600 },
                         { "sez", 0xC700 },
                         { "ses", 0xC800 },
                         { "scc", 0xC900 },
                         { "nop", 0xCA00 },
                         { "wait", 0xCB00 },
                         { "halt", 0xCC00 },
                         { "ret", 0xCD00 },
                         { "push_pc", 0xCE00 },
                         { "pop_pc", 0xCF00 },
                         { "push_flag", 0xD000 },
                         { "pop_flag", 0xD100 },
                         { "reti", 0xD200 },                        
                     };

        public Encodings()
        {

        }
    }
    public class ValueAddress
    {
        int value;
        int address;
        public ValueAddress() { }
        public ValueAddress(int address, int value)
        {
            this.value = value;
            this.address = address;

        }
        public int GetValue() { return value; }
        public int GetAddress() { return address; }
        public String ToString()
        {
            return "[ " + address + ", " + value + " ]";
        }

    }
    public enum ModAdresare { Imediat, Direct, Indirect, Indexat, None, Error };
    //ModAdresare None - pt branch-uri unde este doar un offset
    //nu am ales MA.Imediat pt ca pt acestea se codifica suplimentar inca un cuvant pe cand pt branch-uri nu este nevoie


    public class Operand
    {
        //int index = 0;
        String label = null;
        int reg = -1;
        int op_code = 0;
        int valoare_imediata = 0;   //offset pt branchuri, val imediata MA.Imediat, index MA.Indexat  
        ModAdresare modAdresare = ModAdresare.Error;
        int error_flag = 0;

        public String GetLabel() { return label; }
        public ModAdresare GetModAdresare() { return modAdresare; }
        public int Register() { return reg; }      
        public int GetValImediata() { return valoare_imediata; }        
        public void SetOffset(int offs) { valoare_imediata = offs; }
        public void SetRegister(int r) { reg = r; }
        public void SetModAdresare(ModAdresare m) { modAdresare = m; }

        //@override
        public String ToString()
        {
            return "[ " + modAdresare + ", " + reg + ", " + label + "," + valoare_imediata + " ] ";
        }

        public Operand() { }
        public Operand(String operand1)
        {
            char[] op = operand1.ToCharArray();

            bool indirect_flag = false;
            bool index_flag = false;

            for (int i = 0, j = 1; i < operand1.Length; i++)
            {
                if (op[i] == '(')
                {
                    indirect_flag = true;
                    if (i > 0)
                    {
                        index_flag = true;
                        indirect_flag = false;
                        String st = new String(op[0], 1);
                        for (j = 1; j < i; j++)
                            st += op[j];
                        try
                        {
                            int number = Convert.ToInt32(st);
                            modAdresare = ModAdresare.Indexat;
                            valoare_imediata = number;
                        }
                        catch (FormatException e)
                        {
                            Console.WriteLine(e);
                            modAdresare = ModAdresare.Error;
                        }
                    }
                }

                if (op[i] == ')')
                {
                    if (indirect_flag || index_flag)
                    {
                        if (indirect_flag)
                        {
                            if ((op[1] == 'R') || (op[1] == 'r'))
                            {
                                String st = new String(op[2], 1);
                                for (int k = 3; k < i; k++)
                                    st += op[k];

                                try
                                {
                                    int number = Convert.ToInt32(st);
                                    modAdresare = ModAdresare.Indirect;
                                    reg = number;
                                }
                                catch (FormatException e)
                                {
                                    //Console.WriteLine(e);
                                    modAdresare = ModAdresare.Error;
                                }
                            }
                            else
                            {
                                modAdresare = ModAdresare.Error;
                            }

                        }
                        if (index_flag)
                            if ((op[j + 1] == 'R') || (op[j + 1] == 'r'))
                            {
                                String st = new String(op[j + 2], 1);
                                for (int k = j + 3; k < i; k++)
                                    st += op[k];

                                try
                                {
                                    int number = Convert.ToInt32(st);
                                    modAdresare = ModAdresare.Indexat;
                                    reg = number;
                                }
                                catch (FormatException e)
                                {
                                    Console.WriteLine(e);
                                    modAdresare = ModAdresare.Error;
                                }
                            }
                            else
                            {
                                modAdresare = ModAdresare.None;
                                label = operand1;
                            }
                        }
                    }
                }

            if (index_flag == false && indirect_flag == false)
            {
                try
                {
                    int numb = Convert.ToInt32(operand1);
                    modAdresare = ModAdresare.Imediat;
                    valoare_imediata = numb;
                }
                catch (FormatException)
                {

                    if ((op[0] == 'R') || (op[0] == 'r'))
                    {
                        String st = new String(op[1], 1);
                        for (int k = 2; k < op.Length; k++)
                            st += op[k];

                        try
                        {
                            int number = Convert.ToInt32(st);
                            modAdresare = ModAdresare.Direct;
                            reg = number;
                        }
                        catch (FormatException)
                        {
                            List<ValueAddress> lis;

                            if (Parser.dataList.TryGetValue(operand1, out lis))
                            {


                                // modAdresare = ModAdresare.Imediat;
                                //valoare_imediata = lis[0].GetValue();
                                modAdresare = ModAdresare.Indexat;
                                valoare_imediata = lis[0].GetAddress();
                                reg = 0;

                            }
                            else
                            {
                                modAdresare = ModAdresare.None;
                                label = operand1;


                            }
                        }
                    }
                    else
                    {
                        List<ValueAddress> lis;

                        if (Parser.dataList.TryGetValue(operand1, out lis))
                        {

                            //modAdresare = ModAdresare.None;
                            //label = operand1;
                           modAdresare = ModAdresare.Indexat;
                           valoare_imediata = lis[0].GetAddress();
                           reg = 0;
                        }
                        else
                        {
                            modAdresare = ModAdresare.None;
                            label = operand1;
                        }
                    }
                }
            }

        }

    }

    public class Instruction
    {
        
        static Int32 pc = 0;
        Int32 instr;
        string instrName = "";
        Operand op1 = null;
        Operand op2 = null;
        bool error_flag = false;
        Int32 address = 0;

        public String getName() { return instrName; }
        public Instruction() { }
        public static int IncPC(Instruction i)
        {
            /*if(i.getName() == "call")
                if(i.op2.GetType*/
            if ((GetInstrType(i.instr) == InstrType.B4) ||
                (GetInstrType(i.instr) == InstrType.B3)) return pc + 2;
            if (GetInstrType(i.instr) == InstrType.B1)
            {
                int numb = 0;

                if ((i.op1.GetModAdresare() == ModAdresare.Indexat) ||
                      (i.op1.GetModAdresare() == ModAdresare.Imediat))
                    numb += 2;
                if ((i.op2.GetModAdresare() == ModAdresare.Indexat) ||
                     (i.op2.GetModAdresare() == ModAdresare.Imediat))
                    numb += 2;
                return pc + numb + 2;

            }

            if (GetInstrType(i.instr) == InstrType.B2)
            {
                int numb = 0;
                if (i.op1 != null)
                    if ((i.op1.GetModAdresare() == ModAdresare.Indexat) ||
                          (i.op1.GetModAdresare() == ModAdresare.Imediat) ||
                         ( (i.getName() == "call" )&& (i.op1.GetModAdresare() == ModAdresare.None) ))
                        numb += 2;
                return pc + numb + 2;
            }

            return pc;

        }
        public void SetOperand(Operand o, int index)
        {
            if (index == 1)
                op1 = o;
            if (index == 2)
                op2 = o;

        }
        public static void ResetPc()
        {
            pc = 0;
        }
        public int GetOpcode() { return instr; }
        public Instruction(String str, int addr)
        {
            str = str.ToLower();
            address = addr;

            if (Encodings.instructionDictionary.ContainsKey(str))
            {
                instr = Encodings.instructionDictionary[str];
                instrName = str;
            }
            else
            {
                error_flag = true;
               // output.AppentText("\nInstruction error: " + str);
            }

        }
        public int IndexinLabelDic(List<String> l, string label)
        {
            string str = null;
            for (int i = 0; i < l.Count; i++)
            {
                str = l[i];
                if (str.CompareTo(label) == 0)
                    return i;
            }
            return -1;

        }
        public static String IsLabelDeclaration(String str)
        {

            if (str.EndsWith(":"))
            {
                String label = str.Substring(0, str.Length - 1);
                return label;
            }

            return null;
        }
        public Operand GetFirstOp() { return op1; }
        public Operand GetLastOp() { return op2; }
        public int InstrAddress() { return address; }

        public static InstrType GetInstrType(int instr)
        {

            if ((instr & 0x8000) == 0x00) return InstrType.B1;
            else if ((instr & 0xE000) == 0x8000) return InstrType.B2;
            else if ((instr & 0xE000) == 0xA000) return InstrType.B3;
            else if ((instr & 0xE000) == 0xC000) return InstrType.B4;


            return InstrType.Error;
        }
        /*public static InstrType GetInstrType(UInt16 instr)
        {

            if ((instr & 0x8000) == 0x00) return InstrType.B1;
            else if ((instr & 0xE000) == 0x8000) return InstrType.B2;
            else if ((instr & 0xE000) == 0xA000) return InstrType.B3;
            else if ((instr & 0xE000) == 0xC000) return InstrType.B4;


            return InstrType.Error;
        }*/

        public InstrType GetInstrType()
        {

            if ((instr & 0x8000) == 0x00) return InstrType.B1;
            else if ((instr & 0xE000) == 0x8000) return InstrType.B2;
            else if ((instr & 0xE000) == 0xA000) return InstrType.B3;
            else if ((instr & 0xE000) == 0xC000) return InstrType.B4;


            return InstrType.Error;
        }
        public static InstrType IsInstruction(String str)
        {
            int code;
            if (Encodings.instructionDictionary.ContainsKey(str))
            {
                code = Encodings.instructionDictionary[str];
                Console.WriteLine("instructiune " + str);
                return GetInstrType(code);
            }
            return InstrType.Error;

        }



    }
    public enum InstrType { B1, B2, B3, B4, Error };

    public enum ProgramSector { data, code };

    public class Parser
    {
        //String buffer = null;
        RichTextBox editor = null;
        RichTextBox output = null;
        string commentDelimiter = ";";
        const int data_address = 0;
        const int code_address = 0;
        bool error_flag = false;
        public static int nr_cuvinte = 0;
        public static int nr_cuvinte_date = 0;
        public static Dictionary<String, Int32> labelDictionary = new Dictionary<String, Int32>();
        public static List<Instruction> instructionList = new List<Instruction>();
        public static Dictionary<String, List<ValueAddress>> dataList = new Dictionary<String, List<ValueAddress>>();
        public static Dictionary<int, String> instructionText = new Dictionary<int, string>();

       // public List<Instruction> getInstructionList(){return instructionList;}

        public Parser(RichTextBox rtb, RichTextBox output)
        {
            Clear();
            editor = rtb;
            this.output = output;
            Parse();           
        }

        public void Clear() 
        {
            labelDictionary.Clear();
            instructionList.Clear();
            dataList.Clear();
            instructionText.Clear(); 
        }

        public void Parse() 
        {
            Clear();
            ParserA();
            SetAddress(); 
        }
        

        public void SetAddress()
        {
           
            output.AppendText("------------------------- code sector --------------------------------");
            foreach (Instruction i in instructionList)
            {               
                if (i.GetInstrType() == InstrType.B3 || i.getName() == "call")
                {
                    Operand op = i.GetFirstOp();
                    String l = op.GetLabel();
                    int op_address;
                    if (l != null)
                        if (Parser.labelDictionary.TryGetValue(l, out op_address))
                        {
                            int adresasalt = Parser.labelDictionary[l] - i.InstrAddress();
                          /*  if (adresasalt > 0)
                                op.SetOffset(adresasalt - 2);
                            else
                                op.SetOffset(adresasalt);
                            output.AppendText("eticheta: "+op.GetValImediata());*/
                            if (i.getName() == "call")
                                op.SetOffset(adresasalt - 4);
                            else
                                if (adresasalt > 0)
                                    op.SetOffset(adresasalt - 2);
                                else
                                    op.SetOffset(adresasalt);
                        }
                        else
                            output.AppendText("\n Label: " + l + " does not exist!");
                }
                 output.AppendText("\nAddress: " + i.InstrAddress() + ": " + i.getName());
                if (i.GetFirstOp() != null)
                    output.AppendText(" Op1: " + i.GetFirstOp().ToString());
                if (i.GetLastOp() != null)
                    output.AppendText(" Op2: " + i.GetLastOp().ToString());             

            }
           /*int x = Parser.instructionList.Count;
            if (Parser.instructionList[x - 1].GetOpcode()
                                        != Encodings.instructionDictionary["halt"])
            {
                output.AppendText("\n Halt instruction missing! ");
                error_flag = true;
            }*/
            if (error_flag == false)
            {
                output.ForeColor = Color.Green;
                output.AppendText("\nSuccesful compliation!");
                MessageBox.Show("Succesful compilation!", "Compiler",
                  MessageBoxButtons.OK, MessageBoxIcon.None);
                foreach( KeyValuePair<int, string> blah in instructionText)
                {
                    output.AppendText("\n"+blah.Key + " " + blah.Value);
                }
                
            }
            else 
            {
                output.ForeColor = Color.Red;
                output.AppendText("\n Compilation failed!");
                MessageBox.Show("Compilation failed!", "Compiler",
                  MessageBoxButtons.OK, MessageBoxIcon.Error);
                /*foreach (string s in instructionText)
                    output.AppendText("\n" + s);*/
            }

            
                
        }
        private void ParserA()
        {
            int data_offset = 0;
            int code_offset = 0;
            String line = null;
            char[] separators = { ' ', ',' };
            bool data_flag = false;
            bool code_flag = false;

            for (int ind = 0; ind < editor.Lines.Count(); ind++)
            {

                line = editor.Lines[ind];

                if (IsComent(line) == false)
                {
                    string[] values = line.Split(separators, StringSplitOptions.RemoveEmptyEntries);

                    if (values != null && values.Length != 0)
                    {
                        //verific dc sunt in zona de date
                        if (values[0].CompareTo(".data") == 0)
                        {
                            if (data_flag == false)
                            {
                                data_flag = true;
                            }
                            else
                            {                               
                                output.AppendText(".data already exist");
                                error_flag = true;
                            }
                        }
                        else
                        {
                            if (data_flag == false && code_flag == false)
                            {
                                output.AppendText("error: data sector missing or is inccorect at line: " + ind + "\n");
                                error_flag = true;
                            }
                        }

                        if (values[0].CompareTo(".code") == 0)
                        {
                            data_flag = false;
                            code_flag = true;
                        }
                        if (data_flag)
                        {
                            if (IsLabelDefinition(values[0]))
                            {
                                List<ValueAddress> localMem = new List<ValueAddress>();
                                int j = 1;
                                for (j = 1; j < values.Length && IsNumber(values[j]); j++)
                                {
                                    ValueAddress m = new ValueAddress((data_address + data_offset), Convert.ToInt32(values[j]));
                                    data_offset += 2;
                                    localMem.Add(m);                                   
                                }
                                output.AppendText("data_flag = true Labelvalue: " + ExtractLabel(values[0]));
                                dataList.Add(ExtractLabel(values[0]), localMem);


                                if (j < values.Length - 1)
                                {
                                    output.AppendText("To many declaration " + values[j]);
                                    error_flag = true;
                                }
                           }
                            nr_cuvinte_date = data_offset / 2;

                        }
                        if (code_flag)
                        {
                            int n = -1;
                            for (int i = 0; i < values.Length; i++)
                            {
                                if ((n = IsInstruction(values[i])) > -1)
                                {
                                    Instruction ins = new Instruction(values[i], code_address + code_offset);
                                    instructionText.Add(code_offset, line);
                                    switch (n)
                                    {
                                        case 1:
                                   
                                            if (i + 1 < values.Length)
                                            {
                                                Operand op = new Operand(values[i + 1]);
                                                i += 1;
                                                if (ins.getName() == "call")
                                                {
                                                    op.SetOffset(code_offset);
                                                    op.SetModAdresare(ModAdresare.Imediat);
                                                }
                                                if (ins.GetInstrType() == InstrType.B3 )
                                                {
                                                    op.SetOffset(code_offset);
                                                    
                                                }
                                                //am modificat :D
                                                ins.SetOperand(op, 1);

                                            }
                                            else
                                            {
                                                output.AppendText("error: operand missing " + values[i]);
                                                error_flag = true;
                                            }
                                                break;
                                        case 2:
                                               
                                            if (i + 2 < values.Length)
                                            {
                                                 Operand op1 = new Operand(values[i + 1]);
                                                Operand op2 = new Operand(values[i + 2]);
                                                if(values[i].CompareTo("mov") == 0)
                                                {
                                                    List<ValueAddress> val = new List<ValueAddress>();
                                                    if (op1.GetModAdresare() == ModAdresare.None)
                                                    {

                                                        if (Parser.dataList.TryGetValue(op1.GetLabel(), out val))
                                                        {
                                                            op1.SetRegister(0);
                                                            op1.SetModAdresare(ModAdresare.Indexat);
                                                            op1.SetOffset(val[0].GetAddress());
                                                        }
                                                    }
                                                    if (op2.GetModAdresare() == ModAdresare.None)
                                                    {
                                                        val = new List<ValueAddress>();
                                                        if (Parser.dataList.TryGetValue(op2.GetLabel(), out val))
                                                        {
                                                            op2.SetRegister(0);
                                                            op2.SetModAdresare(ModAdresare.Indexat);
                                                            op2.SetOffset(val[0].GetAddress());
                                                        }
                                                    }
                                                }
                                                
                                               
                                                ins.SetOperand(op1, 1);                                                
                                                ins.SetOperand(op2, 2);

                                                //output.AppendText("\ninstr op1, op2: " + values[i] + values[i + 1] + values[i + 2]
                                                  //           + op1.ToString() + " " + op2.ToString());
                                                i += 2;
                                            }
                                            else
                                            {
                                                output.AppendText("\nerror: operand missing " + values[i]);
                                                error_flag = true;
                                            }
                                                break;
                                        default: break;
                                    }
                                    code_offset += Instruction.IncPC(ins);
                                   // output.AppendText("\ninstruction: " + n + values[i]);
                                    instructionList.Add(ins);
                                }
                                else if (IsLabelDefinition(values[i]))
                                {
                                    Parser.labelDictionary.Add(ExtractLabel(values[i]), code_offset );
                                }
                                else 
                                {
                                    //error_flag = true; 
                                }
                            }
                        }

                    }
                }


            }//for  
            nr_cuvinte= code_offset/2;
        }

        public int IsInstruction(String str)
        {
            InstrType type = Instruction.IsInstruction(str);
            if (type == InstrType.B1) return 2;
            if (type == InstrType.B2) return 1;
            if (type == InstrType.B3)
                return 1;
            if (type == InstrType.B4) return 0;
            return -1;
        }
        public bool IsNumber(String str)
        {
            foreach (char c in str)
                if (Char.IsNumber(c) == false)
                    return false;
            return true;
        }

        public bool IsComent(String line)
        {
            return line.StartsWith(commentDelimiter, true, null);
        }
        public bool IsLabelDefinition(String line)
        {
            return line.EndsWith(":");
        }
        public String ExtractLabel(String str)
        {
            if (IsLabelDefinition(str))
                return str.Substring(0, str.Length - 1);

            return null;
        }
    }
}

